﻿using System.Linq;

using Buddy.BehaviorTree;
using Buddy.CommonBot;
using Buddy.CommonBot.Logic;
using Buddy.Swtor;
using Buddy.Swtor.Objects;

using WingIt;
using WingIt.Dynamics;
using WingIt.Routines.Mirrors;

namespace WingIt.Routines
{
    [RevisionControlId("$Id: Lethality_DirtyFighting.cs 696 2012-08-13 17:20:41Z Chinajade $")]
    public class OperativeLethality_ScoundrelDirtyFighting : MirrorRoutine
    {
        // MAINTAINERS NOTES:
        // * You will notice that lambda expression argument names are unique (e.g., "darkWardOn", "darkWardWhen", "joltWhen"),
        //   instead of generic (e.g., "ctx", "ret", "on", "doWhen", "castWhen").  PLEASE honor this convention!
        //   This allows us to quickly localize problems when looking at user's logs.  When the BehaviorTree encounters an exception
        //   (e.g., "object not found"), the location where an exception is thrown is identifiable only through the name
        //   of the lambda expression argument.  Such is the nature of BehaviorTrees.

        // TODO: Eliminate most of these constants in favor of the ones in Global.
        #region Constants
        private const double RescueResourceLevel = 45;
        private const double DefaultSelfHealingLevel = 75;
        private const double DefaultCompanionHealingLevel = 45;
        private const double DefaultCompanionIsDeadMeHealingLevel = 50;
        private const double CriticalMeHealingLevel = 20;
        private const double CriticalCompanionHealingLevel = 20;
        private const double CriticalResourceLevel = 25;
        #endregion

        [Behavior(BehaviorType.Pull)]
        [Class(CharacterClass.Agent, AdvancedClass.Operative, SkillTreeId.OperativeLethality)]
        [Class(CharacterClass.Smuggler, AdvancedClass.Scoundrel, SkillTreeId.ScoundrelDirtyFighting)]
        public static Composite OperativeLethality_ScoundrelDirtyFighting_Pull()
        {
            return new PrioritySelector(
                LazyRaider.CreateBTPS_NotifyAndShortCircuitWhenCombatDisabled(),
                CreateBTPS.BWcoreFixup_WaitForHarvestComplete(),
                TargetSelect.CreateBTPS_UpdateForBattlefieldConditions(),

                new Decorator(whenCrowdControlNeeded => CCTarget != null,
                    Cast("Slice Droid", sliceDroidOn => CCTarget, sliceDroidWhen => TargetSelect.ViableTargetList().FirstOrDefault(t => t.HasMirrorDebuffStacks("Slice Droid")) == null)),
                BuffSelf("Stealth"),
                CreateBTPS.MoveWithinRangeAndLoS(target => MyTarget, Global.Distance.Melee),
                //Movement.MoveBehind(MyTarget, Global.Distance.Melee),
                Cast("Hidden Strike", hiddenStrikeWhen => Me.HasBuff("Stealth")),
                Cast("Shiv")
            );
        }

        [Behavior(BehaviorType.Combat)]
        [Class(CharacterClass.Agent, AdvancedClass.Operative, SkillTreeId.OperativeLethality)]
        [Class(CharacterClass.Smuggler, AdvancedClass.Scoundrel, SkillTreeId.ScoundrelDirtyFighting)]
        public static Composite OperativeLethality_ScoundrelDirtyFighting_Combat()
        {
            return new PrioritySelector(
                LazyRaider.CreateBTPS_NotifyAndShortCircuitWhenCombatDisabled(),
                TargetSelect.CreateBTPS_UpdateForBattlefieldConditions(),

                CreateBTPS.StopWithinRangeAndLoS(target => MyTarget, Global.Distance.Melee),

                new Decorator(whenCrowdControlNeeded => CCTarget != null,
                    Cast("Slice Droid", sliceDroidOn => CCTarget, sliceDroidWhen => TargetSelect.ViableTargetList().FirstOrDefault(t => t.HasMirrorDebuffStacks("Slice Droid")) == null)),

                // CD's
                Cast("Shield Probe", shieldProbeOn => Me, shieldProbeWhen => Me.HealthPercent <= DefaultSelfHealingLevel), // Absorbs DMG for 15s
                Cast("Evasion", evasionOn => Me, evasionWhen => Me.HealthPercent <= Global.Health.OhShit), // Immune to DMG for 3s
                Cast("Stim Boost", stimBoostOn => Me, stimBoostWhen => Me.HasMirrorBuffStacks("Tactical Advantage", 1)), // Restores Energy over time
                Cast("Adrenaline Probe", adrenalineProbeOn => Me, adrenalineProbeWhen => Resource <= RescueResourceLevel), // Restores 50 Energy
                Cast("Distraction", distractionWhen => MyTarget.Range <= 1f && Global.RequiresCrowdControl), // Interrupt
                Cast("Flash Bang", flashBangWhen => MyTarget.Range <= 1f && Global.RequiresCrowdControl), // Blind/Interrupt
                Cast("Debilitate", debilitateWhen => MyTarget.Range <= Global.Distance.Melee && Global.RequiresCrowdControl), // Stun

                // Do we need to heal ourselves or companion?
                new Decorator(healWhen => (HealTarget != null) && (HealTarget.HealthPercent <= Global.Health.Mid),
                    new PrioritySelector
                    (
                        Cast("Kolto Infusion", koltoInfusionOn => HealTarget, koltoInfusionWhen => Me.HasMirrorBuffStacks("Tactical Advantage", 1)),
                        Cast("Diagnostic Scan", diagnosticScanOn => HealTarget, diagnosticScanWhen => Resource < CriticalResourceLevel),
                        Cast("Kolto Injection", koltoInjectionOn => HealTarget)
                     )),

                //Cast("Hidden Strike", whenStealthed => Me.IsStealthed), // requires behind Target
                Cast("Eviscerate", eviscerateWhen => Global.RequiresCrowdControl),
                Cast("Backstab", backstabWhen => Me.IsBehind(MyTarget)), // Requires behind Target
                Cast("Shiv"),
                Cast("Corrosive Dart", corrosiveDartWhen => !MyTarget.HasDebuff("Poisoned (Tech)") && Global.RequiresCrowdControl),
                //Cast("Orbital Strike", orbitalStrikeWhen => Me.MobCountAround(Global.Distance.Melee) >= 3),
                Cast("Corrosive Grenade", corrosiveGrenadeWhen => Me.MobCountAround(Global.Distance.Melee) >= 3),
                Cast("Fragmentation Grenade", fragmentationGrenadeWhen => Me.MobCountAround(Global.Distance.Melee) >= 3),
                Cast("Carbine Burst", carbineBurstWhen => Me.MobCountAround(Global.Distance.Melee) >= 3 && MyTarget.Range <= 1f && Me.HasMirrorBuffStacks("Tactical Advantage", 2)),
                Cast("Cull", cullWhen => Me.HasMirrorBuffStacks("Tactical Advantage", 2)),
                Cast("Headshot", headShotWhen => MyTarget.IsStunned),
                Cast("Explosive Probe"),
                Cast("Overload Shot", overloadShotWhen => !Me.IsInCover()),
                Cast("Rifle Shot"),

                CreateBTPS.MoveWithinRangeAndLoS(target => MyTarget, Global.Distance.Melee)
                );
        }


        [Behavior(BehaviorType.OutOfCombat)]
        [Class(CharacterClass.Agent, AdvancedClass.Operative, SkillTreeId.OperativeLethality)]
        [Class(CharacterClass.Smuggler, AdvancedClass.Scoundrel, SkillTreeId.ScoundrelDirtyFighting)]
        public static Composite OperativeLethality_ScoundrelDirtyFighting_OutOfCombat()
        {
            // NB: Apparently, BWcore will call this routine when in combat on occasion.  It is also called while we're pulling.
            return (new PrioritySelector(
                TargetSelect.CreateBTPS_UpdateForBattlefieldConditions(),
                new Decorator(oocBuffWhen => !Me.IsMounted,
                    new PrioritySelector(
                        // Only break cover when we're not in pull...
                        new Decorator(breakCoverWhen => !Me.InCombat && !TargetSelect.IsAtThePull(),
                            CreateBTPS.BreakCrouchOrCover())     // Prevents the 'stuck handler dance' if combat ends in crouch/cover
                        ))
                ));
        }
    }
}